#include <render/output/window.h>
#include <app/Application.h>
#include <app/modules/Ainput.h>

#include <GLFW/glfw3.h>
#include <GLES3/gl3.h>


Application *g_app;
Ainput *input;

void keyCallback ( GLFWwindow*, int, int, int, int );
void cursorKeyCallback ( GLFWwindow*, int, int, int );
void cursorPositionCallback ( GLFWwindow*, double, double );
void resizeCallback ( GLFWwindow*, int, int );
void scrollCallback ( GLFWwindow*, double, double );

void initInputDevices(Application* app);

Window::Window ( Application *appPtr ) {
    
    app = appPtr;
    g_app = appPtr;
    
    glfwInit ();
    if ( app -> settings.MSAA > 1 ) {
        glfwWindowHint( GLFW_SAMPLES, app -> settings.MSAA );
    }
    if ( !app -> settings.windowResizeble ) {
        glfwWindowHint ( GLFW_RESIZABLE, false );
    }
    if ( app -> settings.textBlending ) {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }
    
    if ( !app -> settings.fullscreen ) {
        window = glfwCreateWindow ( app -> settings.windowWidth, app -> settings.windowHeight, 
                                    app -> settings.windowName.c_str(), nullptr, nullptr);
    } else {
        window = glfwCreateWindow(glfwGetVideoMode(glfwGetPrimaryMonitor())->width,
                                            glfwGetVideoMode(glfwGetPrimaryMonitor())->height, 
                                            app -> settings.windowName.c_str (),
                                            glfwGetPrimaryMonitor(), nullptr);
    }
    
    glfwMakeContextCurrent ( window );
    
    glfwSetKeyCallback ( window, keyCallback );
    glfwSetMouseButtonCallback ( window, cursorKeyCallback );
    glfwSetCursorPosCallback ( window, cursorPositionCallback );
    glfwSetWindowSizeCallback ( window, resizeCallback );
    glfwSetScrollCallback ( window, scrollCallback );
    
    glEnable ( GL_DEPTH_TEST );
    glEnable ( GL_CULL_FACE );
    glCullFace ( GL_BACK );
    
    input = &( app -> input );

    initInputDevices ( app );
}

bool Window::isClosed () {

    return glfwWindowShouldClose ( window );
}

void Window::Clear ( float R, float G, float B, float A ) {

    glClearColor ( R, G, B, A );
}

void Window::Update () {

    glfwSwapBuffers ( window );
}

Window::~Window () {

    glfwDestroyWindow ( window );
    glfwTerminate ();
}



void keyCallback ( GLFWwindow* window, int key, int scancode, int action, int mods ) {

    input -> keyboard.key = key;
    
    if ( action == GLFW_PRESS ) {
        input -> keyboard.action = PRESSED;
    } else if ( action == GLFW_REPEAT ) {
        input -> keyboard.action = CLICKED;
    } else if ( action == GLFW_RELEASE ) {
        input -> keyboard.action = UNPRESSED;
    }

    g_app -> keyboardCallback ();
}

void cursorKeyCallback ( GLFWwindow* window, int button, int action, int mods ) {

    if ( button == GLFW_MOUSE_BUTTON_LEFT ) {
        input -> mouse.key = MOUSE_LEFT_BUTTON;
    } else if ( button == GLFW_MOUSE_BUTTON_MIDDLE ) {
        input -> mouse.key = MOUSE_MIDDLE_BUTTON;
    } else if ( button == GLFW_MOUSE_BUTTON_RIGHT ) {
        input -> mouse.key = MOUSE_RIGHT_BUTTON;
    }
    
    
    if ( action == GLFW_PRESS ) {
        input -> mouse.action = PRESSED;
    } else if ( action == GLFW_REPEAT ) {
        input -> mouse.action = CLICKED;
    } else if ( action == GLFW_RELEASE ) {
        input -> mouse.action = UNPRESSED;
    }
}


void cursorPositionCallback ( GLFWwindow* window, double xpos, double ypos ) {

    //вычисляем разницу
    input -> mouse.offsetX = xpos - input -> mouse.lastX;
    input -> mouse.offsetY = -( ypos - input -> mouse.lastY );

    //фиксируем последнюю позицию
    input -> mouse.lastX = input -> mouse.posX;
    input -> mouse.lastY = input -> mouse.posY;

    //получаем новую позицию
    input -> mouse.posX = xpos;
    input -> mouse.posY = ypos;

    //умножаем разницу на чувствительность мыши
    input -> mouse.offsetX *= input -> mouse.sensitivity;
    input -> mouse.offsetY *= input -> mouse.sensitivity;

    g_app -> mousePositionCallback ();

}

void resizeCallback ( GLFWwindow* window, int width, int height ) {

    glViewport ( 0, 0, width, height );
}

void scrollCallback ( GLFWwindow* window, double offsetX, double offsetY ) {
    //
}


void initInputDevices ( Application* app ) {

    input -> mouse.lastX = app -> settings.windowWidth / 2;
    input -> mouse.lastY = app -> settings.windowHeight / 2;
}

void Window::grabCursor ( bool value ) {

    if ( value ) {
        glfwSetInputMode ( this -> window, GLFW_CURSOR, GLFW_CURSOR_DISABLED );
    } else {
        glfwSetInputMode ( this -> window, GLFW_CURSOR, GLFW_CURSOR_NORMAL );
    }
}
